home *** CD-ROM | disk | FTP | other *** search
/ Sun Solutions 1997 April to September / Sun Solutions CD - APR '97 - SEP '97 (704-3778-12 Rev. H)(Sun Microsystems, Inc.)(1997).iso / products / .wais / Solaris_2 / Benchmark.pm < prev    next >
Text File  |  1995-12-11  |  7KB  |  246 lines

  1. package Benchmark;
  2.  
  3. # Purpose: benchmark running times of code.
  4. #
  5. #
  6. # Usage - to time code snippets and print results:
  7. #
  8. #    timethis($count, '...code...');
  9. #        
  10. # prints:
  11. #    timethis 100:  2 secs ( 0.23 usr  0.10 sys =  0.33 cpu)
  12. #
  13. #
  14. #    timethese($count, {
  15. #        Name1 => '...code1...',
  16. #        Name2 => '...code2...',
  17. #        ... });
  18. # prints:
  19. #    Benchmark: timing 100 iterations of Name1, Name2...
  20. #         Name1:  2 secs ( 0.50 usr  0.00 sys =  0.50 cpu)
  21. #         Name2:  1 secs ( 0.48 usr  0.00 sys =  0.48 cpu)
  22. #
  23. # The default display style will automatically add child process
  24. # values if non-zero.
  25. #
  26. #
  27. # Usage - to time sections of your own code:
  28. #
  29. #    use Benchmark;
  30. #    $t0 = new Benchmark;
  31. #    ... your code here ...
  32. #    $t1 = new Benchmark;
  33. #    $td = &timediff($t1, $t0);
  34. #    print "the code took:",timestr($td),"\n";
  35. #
  36. #    $t = &timeit($count, '...other code...')
  37. #    print "$count loops of other code took:",timestr($t),"\n";
  38. #
  39. # Data format:
  40. #       The data is stored as a list of values from the time and times
  41. #       functions: ($real, $user, $system, $children_user, $children_system)
  42. #    in seconds for the whole loop (not divided by the number of rounds).
  43. #        
  44. # Internals:
  45. #    The timing is done using time(3) and times(3).
  46. #        
  47. #    Code is executed in the callers package
  48. #
  49. #    Enable debugging by:  $Benchmark::debug = 1;
  50. #
  51. #    The time of the null loop (a loop with the same
  52. #    number of rounds but empty loop body) is substracted
  53. #    from the time of the real loop.
  54. #
  55. #    The null loop times are cached, the key being the
  56. #    number of rounds. The caching can be controlled using
  57. #    &clearcache($key); &clearallcache;
  58. #    &disablecache; &enablecache;
  59. #
  60. # Caveats:
  61. #
  62. #    The real time timing is done using time(2) and
  63. #    the granularity is therefore only one second.
  64. #
  65. #    Short tests may produce negative figures because perl
  66. #    can appear to take longer to execute the empty loop 
  67. #    than a short test: try timethis(100,'1');
  68. #
  69. #    The system time of the null loop might be slightly
  70. #    more than the system time of the loop with the actual
  71. #    code and therefore the difference might end up being < 0
  72. #
  73. #    More documentation is needed :-(
  74. #    Especially for styles and formats.
  75. #
  76. # Authors:    Jarkko Hietaniemi <Jarkko.Hietaniemi@hut.fi>
  77. #         Tim Bunce <Tim.Bunce@ig.co.uk>
  78. #
  79. #
  80. # Last updated:    Sept 8th 94 by Tim Bunce
  81. #
  82.  
  83. use Exporter;
  84. @ISA=(Exporter);
  85. @EXPORT=qw(timeit timethis timethese timediff timestr);
  86. @EXPORT_OK=qw(clearcache clearallcache disablecache enablecache);
  87.  
  88. &init;
  89.  
  90. sub init {
  91.     $debug = 0;
  92.     $min_count = 4;
  93.     $min_cpu   = 0.4;
  94.     $defaultfmt = '5.2f';
  95.     $defaultstyle = 'auto';
  96.     # The cache can cause a slight loss of sys time accuracy. If a
  97.     # user does many tests (>10) with *very* large counts (>10000)
  98.     # or works on a very slow machine the cache may be useful.
  99.     &disablecache;
  100.     &clearallcache;
  101. }
  102.  
  103. sub clearcache    { delete $cache{$_[0]}; }
  104. sub clearallcache { %cache = (); }
  105. sub enablecache   { $cache = 1; }
  106. sub disablecache  { $cache = 0; }
  107.  
  108.  
  109. # --- Functions to process the 'time' data type
  110.  
  111. sub new { my(@t)=(time, times); print "new=@t\n" if $debug; bless \@t; }
  112.  
  113. sub cpu_p { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $pu+$ps         ; }
  114. sub cpu_c { my($r,$pu,$ps,$cu,$cs) = @{$_[0]};         $cu+$cs ; }
  115. sub cpu_a { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $pu+$ps+$cu+$cs ; }
  116. sub real  { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $r              ; }
  117.  
  118. sub timediff{
  119.     my($a, $b) = @_;
  120.     my(@r);
  121.     for($i=0; $i < @$a; ++$i){
  122.     push(@r, $a->[$i] - $b->[$i]);
  123.     }
  124.     bless \@r;
  125. }
  126.  
  127. sub timestr{
  128.     my($tr, $style, $f) = @_;
  129.     my(@t) = @$tr;
  130.     warn "bad time value" unless @t==5;
  131.     my($r, $pu, $ps, $cu, $cs) = @t;
  132.     my($pt, $ct, $t) = ($tr->cpu_p, $tr->cpu_c, $tr->cpu_a);
  133.     $f = $defaultfmt unless $f;
  134.     # format a time in the required style, other formats may be added here
  135.     $style = $defaultstyle unless $style;
  136.     $style = ($ct>0) ? 'all' : 'noc' if $style=~/^auto$/;
  137.     my($s) = "@t $style"; # default for unknown style
  138.     $s=sprintf("%2d secs (%$f usr %$f sys + %$f cusr %$f csys = %$f cpu)",
  139.                 @t,$t) if $style =~ /^all$/;
  140.     $s=sprintf("%2d secs (%$f usr %$f sys = %$f cpu)",
  141.                 $r,$pu,$ps,$pt) if $style =~ /^noc$/;
  142.     $s=sprintf("%2d secs (%$f cusr %$f csys = %$f cpu)",
  143.                 $r,$cu,$cs,$ct) if $style =~ /^nop$/;
  144.     $s;
  145. }
  146. sub timedebug{
  147.     my($msg, $t) = @_;
  148.     print STDERR "$msg",timestr($t),"\n" if ($debug);
  149. }
  150.  
  151.  
  152. # --- Functions implementing low-level support for timing loops
  153.  
  154. sub runloop {
  155.     my($n, $c) = @_;
  156.     my($t0, $t1, $td); # before, after, difference
  157.  
  158.     # find package of caller so we can execute code there
  159.     my ($curpack) = caller(0);
  160.     my ($i, $pack)= 0;
  161.     while (($pack) = caller(++$i)) {
  162.     last if $pack ne $curpack;
  163.     }
  164.  
  165.     my $subcode = "sub { package $pack; my(\$_i)=$n; while (\$_i--){$c;} }";
  166.     my $subref  = eval $subcode;
  167.     die "runloop unable to compile '$c': $@\ncode: $subcode\n" if $@;
  168.     print STDERR "runloop $n '$subcode'\n" if ($debug);
  169.  
  170.     $t0 = &new;
  171.     &$subref;
  172.     $t1 = &new;
  173.     $td = &timediff($t1, $t0);
  174.  
  175.     timedebug("runloop:",$td);
  176.     $td;
  177. }
  178.  
  179.  
  180. sub timeit {
  181.     my($n, $code) = @_;
  182.     my($wn, $wc, $wd);
  183.  
  184.     printf STDERR "timeit $n $code\n" if $debug;
  185.  
  186.     if ($cache && exists $cache{$n}){
  187.     $wn = $cache{$n};
  188.     }else{
  189.     $wn = &runloop($n, '');
  190.     $cache{$n} = $wn;
  191.     }
  192.  
  193.     $wc = &runloop($n, $code);
  194.  
  195.     $wd = timediff($wc, $wn);
  196.  
  197.     timedebug("timeit: ",$wc);
  198.     timedebug("      - ",$wn);
  199.     timedebug("      = ",$wd);
  200.  
  201.     $wd;
  202. }
  203.  
  204.  
  205. # --- Functions implementing high-level time-then-print utilities
  206.  
  207. sub timethis{
  208.     my($n, $code, $title, $style) = @_;
  209.     my($t) = timeit($n, $code);
  210.     local($|) = 1;
  211.     $title = "timethis $n" unless $title;
  212.     $style = "" unless $style;
  213.     printf("%10s: ", $title);
  214.     print timestr($t, $style),"\n";
  215.     # A conservative warning to spot very silly tests.
  216.     # Don't assume that your benchmark is ok simply because
  217.     # you don't get this warning!
  218.     print "            (warning: too few iterations for a reliable count)\n"
  219.     if (   $n < $min_count
  220.         || ($t->real < 1 && $n < 1000)
  221.         || $t->cpu_a < $min_cpu);
  222.     $t;
  223. }
  224.  
  225.  
  226. sub timethese{
  227.     my($n, $alt, $style) = @_;
  228.     die "usage: timethese(count, { 'Name1'=>'code1', ... }\n"
  229.         unless ref $alt eq HASH;
  230.     my(@all);
  231.     my(@names) = sort keys %$alt;
  232.     $style = "" unless $style;
  233.     print "Benchmark: timing $n iterations of ",join(', ',@names),"...\n";
  234.     foreach(@names){
  235.     $t = timethis($n, $alt->{$_}, $_, $style);
  236.     push(@all, $t);
  237.     }
  238.     # we could produce a summary from @all here
  239.     # sum, min, max, avg etc etc
  240.     @all;
  241. }
  242.  
  243.  
  244. 1;
  245.